# -*- coding: utf-8 -*-
# @Author: bao
# @Date:   2017-02-17 11:20:24
# @Last Modified by:   bao
# @Last Modified time: 2017-05-14 19:07:49

from gen_topo import *
from gen_topo_fully import *
from datetime import datetime

class gen_topo_detour(gen_topo_fully):
	"""
	non-blocking circuit switch with forwarding node constraint (xy, yx)
	"""
	def __init__(self):
		gen_topo_fully.__init__(self)
		self.pre_forwards = {} # {port:{(src1,dst1):forwarding_cp, (src2,dst2):forwarding_cp}}

	def _transform(self, wl_graph, tf_graph, degree, base_len):
		'''
		Generate new topology based on multi max_weight_matching (directed bipartite graph)
		node (bp_src, bipartite=0, degree=0)
		node (bp_dst, bipartite=0, degree=0)
		edge (src, dst, 'weight')
		'''
		remain_edges = []
		temp_graph = tf_graph.copy()
		while temp_graph.edges():
			match = nx.max_weight_matching(temp_graph, maxcardinality=False)
			#print match
			for src in match:
				dst = match[src]
				if src <= base_len:
					#print src, dst, temp_graph[src][dst]['weight']
					max_degree = max(wl_graph.node[src]['degree'], wl_graph.node[dst]['degree'])
					if max_degree < degree:
						traffic_weight = get_weight(temp_graph[src][dst]['weight'])
						wl_max = degree - max_degree
						wl_num = min(traffic_weight, wl_max)
						wl_graph.add_edge(src, dst, weight=wl_num)
						wl_graph.node[src]['degree'] += wl_num
						wl_graph.node[dst]['degree'] += wl_num
					else:
						remain_edges.append((src, dst))
					temp_graph.remove_edge(src, dst)

	def _wavelength_adjustment(self, new_matches, d_matches, s_matches, row_size, base_len):
		'''
		The new_matches are from minimal wavelength adjustment algorithm (Wavecube)
		Update the new_matches, d_matches(dynamic) and s_matches(static)
		cur_forward : {(11, 24): 28, (6, 20): 24, (1, 32): 20, (13, 26): 30}
		'''
		starttime = datetime.now()
		for x in new_matches:
			if x not in self.pre_forwards:
				self.pre_forwards[x] = {}
			cur_forward, cp_set_xy, cp_set_yx = self._non_conflicting(new_matches[x], self.pre_forwards[x], row_size, base_len)

			# update d_matches, s_matches and pre_forwards
			for k in cur_forward:
				if k not in self.pre_forwards[x] or self.pre_forwards[x][k] != cur_forward[k]:
					
					#if self.pre_forwards[x][k] != cur_forward[k]:
					#	print "!!!!"
					
					if k not in d_matches[x]:
						#print "$$$"
						d_matches[x].append(k)
						s_matches[x].remove(k)

			self.pre_forwards[x] = cur_forward.copy()
		endtime = datetime.now()
		timediff = endtime - starttime
		print 'wavelength_adjustment time ',timediff
	
	def _non_conflicting(self, match, pre_forward, row_size, base_len):
		cur_forward = {}
		cp_set_xy = {}
		cp_set_yx = {}
		#print len(match), match
		for (src,dst) in match:
			(src_axis_x, src_axis_y) = get_cd(src, row_size)
			(dst_axis_x, dst_axis_y) = get_cd(dst-base_len, row_size)
			(Is_same_axis, cp_x, cp_y) = get_cp((src_axis_x, src_axis_y), (dst_axis_x, dst_axis_y), row_size, base_len)
			#print (Is_same_axis, cp_x, cp_y)
			if not Is_same_axis:
				if (src,dst) not in pre_forward:
					pre_forward[(src,dst)] = 0

				if cp_x not in cp_set_xy:
					if pre_forward[(src,dst)] == 0 or pre_forward[(src,dst)] == cp_x:
						cp_set_xy[cp_x] = (src, dst)
						cur_forward[(src,dst)] = cp_x
					elif cp_y not in cp_set_yx:
						cp_set_yx[cp_y] = (src, dst)
						cur_forward[(src,dst)] = cp_y
					else:
						cp_set_xy[cp_x] = (src, dst)
						cur_forward[(src,dst)] = cp_x
				elif cp_y not in cp_set_yx:
					cp_set_yx[cp_y] = (src, dst)
					cur_forward[(src,dst)] = cp_y
				else:
					counter = []
					
					self._adjust(cp_set_xy, cp_set_yx, cur_forward, src, dst, cp_x, 'xy', base_len, row_size, counter)

					if len(counter) > 100:
						counter = []
						self._adjust(cp_set_xy, cp_set_yx, cur_forward, src, dst, cp_y, 'yx', base_len, row_size, counter)
					if len(counter) > 100:
						print "error"
					#print "conflict"
		'''
		print cur_forward
		print 'cp_set_xy', cp_set_xy
		print 'cp_set_yx', cp_set_yx
		'''
		return cur_forward, cp_set_xy, cp_set_yx

	def _adjust(self, cp_set_xy, cp_set_yx, cur_forward, src, dst, cp, direction, base_len, row_size, counter):
		'''
		Adjust the colors of cp in detour network
		@ cp cp
		@ direction xy or yx
		@ counter : indicates how many times recursion
		'''
		counter.append(1)
		#print counter
		#print (src,dst,direction,cp)
		if direction == 'xy':
			edge = cp_set_xy[cp]
		else:
			edge = cp_set_yx[cp]

		(src_axis_x, src_axis_y) = get_cd(edge[0], row_size)
		(dst_axis_x, dst_axis_y) = get_cd(edge[1]-base_len, row_size)
		(Is_same_axis, cp_x, cp_y) = get_cp((src_axis_x, src_axis_y), (dst_axis_x, dst_axis_y), row_size, base_len)

		if len(counter) > 100: #another direction
			return

		if direction == 'xy':
			if cp_y not in cp_set_yx:
				cp_set_yx[cp_y] = (edge[0], edge[1])
				cp_set_xy[cp] = (src, dst)
				cur_forward[(edge[0], edge[1])] = cp_y
				cur_forward[(src, dst)] = cp
				return
			else:
				self._adjust(cp_set_xy, cp_set_yx, cur_forward, edge[0], edge[1], cp_y, 'yx', base_len, row_size, counter)
				cp_set_yx[cp_y] = (edge[0], edge[1])
				cp_set_xy[cp] = (src, dst)
				cur_forward[(edge[0], edge[1])] = cp_y
				cur_forward[(src, dst)] = cp
		else:
			if cp_x not in cp_set_xy:
				cp_set_xy[cp_x] = (edge[0], edge[1])
				cp_set_yx[cp] = (src, dst)
				cur_forward[(edge[0], edge[1])] =cp_x
				cur_forward[(src, dst)] = cp
				return
			else:
				self._adjust(cp_set_xy, cp_set_yx, cur_forward, edge[0], edge[1], cp_x, 'xy', base_len, row_size, counter)
				cp_set_xy[cp_x] = (edge[0], edge[1])
				cp_set_yx[cp] = (src, dst)
				cur_forward[(edge[0], edge[1])] =cp_x
				cur_forward[(src, dst)] = cp
				